#include <atlbase.h>
#include <Exdisp.h>
#include <SHLGUID.h>

#include <string>
#include <sstream>
#include <cstdio>

#include "detours.h"
#include "Miscellaneous.h"
#include "FileVersion.h"
#pragma comment(lib, "detours.lib")

using namespace std;

CMiscellaneous::CMiscellaneous(void)
{
}


CMiscellaneous::~CMiscellaneous(void)
{
}


//------------------------------------------------------------------------
// hook DialogBoxParamW@user32.dll
static INT_PTR (WINAPI* DialogBoxParamNext)(
	__in_opt HINSTANCE hInstance,
	__in LPCWSTR lpTemplateName,
	__in_opt HWND hWndParent,
	__in_opt DLGPROC lpDialogFunc,
	__in LPARAM dwInitParam
		) = DialogBoxParamW;

INT_PTR
WINAPI
DialogBoxParamCallback(
	__in_opt HINSTANCE hInstance,
	__in LPCWSTR lpTemplateName,
	__in_opt HWND hWndParent,
	__in_opt DLGPROC lpDialogFunc,
	__in LPARAM dwInitParam
	)
{		
	if(4009 == (int)lpTemplateName)
		return 1;
	else
		return DialogBoxParamNext(
				hInstance,
				lpTemplateName,
				hWndParent,
				lpDialogFunc,
				dwInitParam
			);
}
//------------------------------------------------------------------------


wstring CMiscellaneous::GetSettingFile(void) throw(...)
{
	static wchar_t szModule[] = L"cmb-china";
	static wchar_t szSetting[] = L"setting.xml";
	wchar_t szPath[MAX_PATH];

	if(GetModuleFileNameW(GetModuleHandleW(szModule), szPath, MAX_PATH)
		== 0)
	{
		wostringstream os;
		os << L"Failed to get the location of cmb-china.dll. Error code: " << GetLastError();
		throw os.str();
	}

	wstring path = szPath;
	auto lastBackSlash = path.find_last_of(L'\\');

	return path.substr(0, lastBackSlash + 1) + szSetting;
}

wstring CMiscellaneous::GetAttributeValue(IXmlReader* pReader, const wchar_t* key) throw(...)
{
	HRESULT hr;
	const WCHAR* pwszBuffer;

	hr = pReader->MoveToAttributeByName(key, NULL);
	if(FAILED(hr))
		throw wstring(L"Failed to move to attribute ->") + key;

	hr = pReader->GetValue(&pwszBuffer, NULL);
	if(FAILED(hr))
		throw wstring(L"Failed to get value of attribute ->") + key;

	hr = pReader->MoveToElement();
	if(FAILED(hr))
		throw wstring(L"Failed to move back to element when getting attribute ->") + key;

	return wstring(pwszBuffer);
}

wstring CMiscellaneous::GetLocalName(IXmlReader* pReader) throw(...)
{
	HRESULT hr;
	const WCHAR* pwszBuffer;

	hr = pReader->GetLocalName(&pwszBuffer, NULL);
	if(FAILED(hr))
		throw wstring(L"Failed to get local name");

	return wstring(pwszBuffer);
}

wstring CMiscellaneous::GetValue(IXmlReader* pReader) throw(...)
{
	HRESULT hr;
	const WCHAR* pwszBuffer;

	hr = pReader->GetValue(&pwszBuffer, NULL);
	if(FAILED(hr))
		throw wstring(L"Failed to get value");

	return wstring(pwszBuffer);
}


pair<wstring, wstring> CMiscellaneous::GetAttributePair(IXmlReader* pReader) throw(...)
{
	return pair<wstring, wstring>(GetLocalName(pReader), GetValue(pReader));
}

void CMiscellaneous::RaiseEvent(IHTMLElement* e, const wstring& _event) throw(...)
{
	if(_event == L"false")
		return;

	HRESULT hr;
	CComQIPtr<IHTMLElement3> spEl3;
	VARIANT_BOOL varbResult;  
	CComVariant varEvent;

	spEl3 = e;
	if(!spEl3)
		throw wstring(L"Cannot query interface IHTMLElement3 when raising an event ->") + _event;

	hr = spEl3->fireEvent(CComBSTR(_event.c_str()), &varEvent, &varbResult);
	if(FAILED(hr) || !varbResult)
		throw wstring(L"Failed to raise an event ->") + _event;
}

//auto s0 = CMiscellaneous::Split(L"", L'-');		-> { '' }
//auto s1 = CMiscellaneous::Split(L"-", L'-');		-> { '', '' }
//auto s2 = CMiscellaneous::Split(L"--", L'-');		-> { '', '', '' }
//auto s3 = CMiscellaneous::Split(L"0", L'-');		-> { '0' }
//auto s4 = CMiscellaneous::Split(L"00", L'-');		-> { '00' }
//auto s5 = CMiscellaneous::Split(L"00-", L'-');	-> { '00', '' }
//auto s6 = CMiscellaneous::Split(L"-00", L'-');	-> { '', '00' }
//auto s7 = CMiscellaneous::Split(L"-0-0", L'-');	-> { '', '0', '0' }
//auto s8 = CMiscellaneous::Split(L"0-0-0", L'-');	-> { '0', '0', '0' }
unique_ptr<vector<wstring>> CMiscellaneous::Split(const wstring& source, wchar_t delimiter) throw()
{
	unique_ptr<vector<wstring>> upvs(new vector<wstring>());

	wstring::size_type p, q = 0;
	
	while((p = source.find_first_of(delimiter, q)) != wstring::npos)
	{
		upvs->push_back(source.substr(q, p - q));
		q = p + 1;
	}
	upvs->push_back(source.substr(q));
	
	return upvs;
}

void CMiscellaneous::SuppressSecurityWarning() throw(...)
{
	DetourTransactionBegin();
	DetourUpdateThread(GetCurrentThread());
	DetourAttach((PVOID*)&DialogBoxParamNext, DialogBoxParamCallback);
	if(NO_ERROR != DetourTransactionCommit())
		throw new wstring(L"Failed to attach DialogBoxParam");
}

void CMiscellaneous::DesuppressSecurityWarning() throw(...)
{
	DetourTransactionBegin();
	DetourUpdateThread(GetCurrentThread());
	DetourDetach((PVOID*)&DialogBoxParamNext, DialogBoxParamCallback);
	if(NO_ERROR != DetourTransactionCommit())
		throw new wstring(L"Failed to detach DialogBoxParam");
}



BOOL CALLBACK EnumIESProc(HWND hwnd,LPARAM lParam)
{
	TCHAR	buf[100];

	::GetClassName( hwnd, (LPTSTR)&buf, 100 );
	if ( _tcscmp( buf, _T("Internet Explorer_Server") ) == 0 )
	{
		*(HWND*)lParam = hwnd;
		return FALSE;
	}
	else
		return TRUE;
}

BOOL CALLBACK KillIEOProc(HWND hwnd,LPARAM lParam)
{
	TCHAR	buf[100];

	::GetClassName( hwnd, (LPTSTR)&buf, 100 );
	if ( _tcscmp( buf, _T("Internet Explorer_ObjectOverlay") ) == 0 )
	{
		SendMessageW(hwnd, WM_CLOSE, 0, 0);
		//MessageBoxW(0, buf, 0,0);
	}

	return TRUE;
}

void CMiscellaneous::WorkaroundForIE7(IHTMLDocument2* document) throw(...)
{
	// IE7
	//  Send space or enter key to activate the control -> terminate Class=Internet Explorer_ObjectOverlay
	HRESULT hr;
	CComQIPtr<IHTMLWindow2> spWindow;
	CComQIPtr<IServiceProvider> spProvider;
	CComQIPtr<IWebBrowser2> spBrowser;
	long hwnd;

	hr = document->get_parentWindow(&spWindow);
	spProvider = spWindow;
	hr = spProvider->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, reinterpret_cast<void **>(&spBrowser));
	if (FAILED(hr) || !spBrowser)
		throw wstring(L"Failed to QueryService->SID_SWebBrowserApp");
	
	hr = spBrowser->get_HWND(&hwnd);
	if (FAILED(hr))
		throw wstring(L"Failed to get_HWND");

	
	// Find Internet Explorer_Server
	HWND hWndIES = NULL;
	EnumChildWindows((HWND)hwnd, EnumIESProc, (LPARAM)&hWndIES);

	if ( !hWndIES )
		throw wstring(L"Failed to Internet Explorer_Server");

	// Kill Internet Explorer_ObjectOverlay
	EnumChildWindows(hWndIES, KillIEOProc, NULL);
}

int CMiscellaneous::GetIEMajorVersion() throw(...)
{
	wostringstream os;
	os << _wgetenv(L"programfiles");
	os << L"\\Internet Explorer\\iexplore.exe";

	return CFileVersion::GetVersion(os.str())->Major;
}